package com.wangdong.wdutils.utils;

import android.util.Base64;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

public class DLRSAUtil {

    private static final String CHARSET = "UTF-8";
    private static final String RSA_ALGORITHM = "RSA";
    //构建Cipher实例时所传入的的字符串，默认为"RSA/NONE/PKCS1Padding"
    private static String sTransform = "RSA/NONE/PKCS1Padding";

    //进行Base64转码时的flag设置，默认为Base64.DEFAULT
    private static int sBase64Mode = Base64.DEFAULT;

    public static Map<String, String> createKeys(int keySize) {

        //生成密匙对
        KeyPair keyPair = generateRSAKeyPair(keySize);
        //得到公钥
        Key publicKey = keyPair.getPublic();
        String publicKeyStr = DLEncodeUtil.base64Encode2String(publicKey.getEncoded());
        //得到私钥
        Key privateKey = keyPair.getPrivate();
        String privateKeyStr = DLEncodeUtil.base64Encode2String(privateKey.getEncoded());
        Map<String, String> keyPairMap = new HashMap<String, String>();
        keyPairMap.put("publicKey", publicKeyStr);
        keyPairMap.put("privateKey", privateKeyStr);

        return keyPairMap;
    }
    /*
        产生密钥对
        @param keyLength
        密钥长度，小于1024长度的密钥已经被证实是不安全的，通常设置为1024或者2048，建议2048
     */
    public static KeyPair generateRSAKeyPair(int keyLength){
        KeyPair keyPair = null;
        try {

            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA_ALGORITHM);
            //设置密钥长度
            keyPairGenerator.initialize(keyLength);
            //产生密钥对
            keyPair = keyPairGenerator.generateKeyPair();

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }

        return keyPair;
    }
    /*
        加密或解密数据的通用方法
        @param srcData
        待处理的数据
        @param key
        公钥或者私钥
        @param mode
        指定是加密还是解密，值为Cipher.ENCRYPT_MODE或者Cipher.DECRYPT_MODE

     */
    private static byte[] processData(byte[] srcData, Key key,int mode){

        //用来保存处理结果
        byte[] resultBytes = null;

        try {

            //构建Cipher对象，需要传入一个字符串，格式必须为"algorithm/mode/padding"或者"algorithm/",意为"算法/加密模式/填充方式"
            Cipher cipher = Cipher.getInstance(sTransform);
            //初始化Cipher，mode指定是加密还是解密，key为公钥或私钥
            cipher.init(mode,key);
            //处理数据
            resultBytes = cipher.doFinal(srcData);

//            resultBytes = rsaSplitCodec(cipher,mode,srcData,key.getEncoded().length);

        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | BadPaddingException | IllegalBlockSizeException e) {
            e.printStackTrace();
        }
        return resultBytes;
    }

    /**
     *   使用公钥加密数据，结果用Base64转码
     * @param srcData   byty[]
     * @param publicKey  PublicKey
     * @return String
     */
    public static String encryptDataByPublicKey(byte[] srcData, PublicKey publicKey){

        byte[] resultBytes = processData(srcData,publicKey,Cipher.ENCRYPT_MODE);

        return Base64.encodeToString(resultBytes,sBase64Mode);

    }

    /*
        使用私钥解密，返回解码数据
     */
    public static byte[] decryptDataByPrivate(String encryptedData, PrivateKey privateKey){

        byte[] bytes = Base64.decode(encryptedData,sBase64Mode);

        return processData(bytes,privateKey,Cipher.DECRYPT_MODE);
    }

    /*
        使用私钥进行解密，解密数据转换为字符串，使用utf-8编码格式
     */
    public static String decryptedToStrByPrivate(String encryptedData, PrivateKey privateKey){
        return new String(decryptDataByPrivate(encryptedData,privateKey));
    }

    /*
        使用私钥解密，解密数据转换为字符串，并指定字符集
     */
    public static String decryptedToStrByPrivate(String encryptedData, PrivateKey privateKey,String charset){
        try {

            return new String(decryptDataByPrivate(encryptedData,privateKey),charset);

        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        return null;
    }

    /*
        使用私钥加密，结果用Base64转码
     */

    public static String encryptDataByPrivateKey(byte[] srcData,PrivateKey privateKey){

        byte[] resultBytes = processData(srcData,privateKey,Cipher.ENCRYPT_MODE);

        return Base64.encodeToString(resultBytes,sBase64Mode);
    }


    /*
        使用公钥解密，返回解密数据
     */

    public static byte[] decryptDataByPublicKey(String encryptedData,PublicKey publicKey){

        byte[] bytes = Base64.decode(encryptedData,sBase64Mode);

        return processData(bytes,publicKey,Cipher.DECRYPT_MODE);

    }

    /*
        使用公钥解密，结果转换为字符串，使用默认字符集utf-8
     */
    public static String decryptedToStrByPublicKey(String encryptedData,PublicKey publicKey){
        return new String(decryptDataByPublicKey(encryptedData,publicKey));
    }

    /*
        使用公钥解密，结果转换为字符串，使用指定字符集
     */
    public static String decryptedToStrByPublicKey(String encryptedData,PublicKey publicKey,String charset){
        try {

            return new String(decryptDataByPublicKey(encryptedData,publicKey),charset);

        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        return null;
    }

    /*
        将字符串形式的公钥转换为公钥对象
     */

    public static PublicKey keyStrToPublicKey(String publicKeyStr){

        PublicKey publicKey = null;

        byte[] keyBytes = Base64.decode(publicKeyStr,sBase64Mode);

        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);

        try {

            KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);

            publicKey = keyFactory.generatePublic(keySpec);

        } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
            e.printStackTrace();
        }

        return publicKey;

    }

    /*
        将字符串形式的私钥，转换为私钥对象
     */

    public static PrivateKey keyStrToPrivate(String privateKeyStr){

        PrivateKey privateKey = null;

        byte[] keyBytes = Base64.decode(privateKeyStr,sBase64Mode);

        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);

        try {

            KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);

            privateKey = keyFactory.generatePrivate(keySpec);

        } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
            e.printStackTrace();
        }

        return privateKey;

    }

    private static byte[] rsaSplitCodec(Cipher cipher, int opmode, byte[] datas, int keySize) {
        int maxBlock = 0;
        if (opmode == Cipher.DECRYPT_MODE) {
            maxBlock = keySize / 8;
        } else {
            maxBlock = keySize / 8 - 11;
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] buff;
        int i = 0;
        try {
            while (datas.length > offSet) {
                if (datas.length - offSet > maxBlock) {
                    buff = cipher.doFinal(datas, offSet, maxBlock);
                } else {
                    buff = cipher.doFinal(datas, offSet, datas.length - offSet);
                }
                out.write(buff, 0, buff.length);
                i++;
                offSet = i * maxBlock;
            }
        } catch (Exception e) {
            throw new RuntimeException("加解密阀值为[" + maxBlock + "]的数据时发生异常", e);
        }
        byte[] resultDatas = out.toByteArray();
        try {
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return resultDatas;
    }

    public static void main(String[] args) {

        Map<String, String> tempKeys = createKeys(2048);

        String tpub = tempKeys.get("publicKey");
        String tpri = tempKeys.get("privateKey");

        System.out.println("publicKey:"+tpub);
        System.out.println("privateKey:"+tpri);

        /**
         * 使用私钥加密，结果用Base64转码
         */
        String tempeStr = DLRSAUtil.encryptDataByPrivateKey("123".getBytes(),keyStrToPrivate(tpri));

        System.out.println("tempeStr:"+tempeStr);

//        String pub = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkM_BVSx2UHxnFwrYOW632jA8JjXb7h7D37HaEnlpeJTb-Vh_blGe68FdyLqH_rCuVbaiWAYCSVM8l5dzYBWtzCB8IpN0OMk5tbKn0ReAPoqNnWHul1wfJ6Cw3HM4Dw3hWbNp4lQ9u-pnedkQzWpWOVKj4oDrsK49zlEN2HRlavajBHF6c04I8xEjiXt7TznYlHHRlwHJVg46ORAzkpwH-r4QaJEd7NMjdZEEUTy5_-GM_RICU0HdBLB8EY8e6_ZHled0r7B51rhZdkyJ7VVAoALCAYxFDkvSW5hXZLGInr3ldHCBjaYc2P-wUbj4SNN1ZpYmwh3-IVubHYBiah3xCQIDAQAB";

        String encodedData=tempeStr;//"EuRkXoTyW0eI3eoY7WW7NsWBglCYFpMtCfcL0t2zJbCC8L7kGz7Id8j6X0ztc-JTWwQG_G4jLz3wZv8V3WCxgrniE2Cl2ipRlXUUDvtgfAQrrwZD2ABCw7KzqWugFcRSmVbQEgK7jeA6dLfwGCDButMHrXVN2bsxBEFSzjEkMvCiRam1o_W0iEcBI8dnpI6s66EH4FWKFMJUFwuaWarWnDfBlQxa_3_QwdbmgsVhxViFLwzclfMt19QtmIu9ca2Yy5CgC-dyvlHfcXiVDcReZZJLZ3R-6MPQTEqnU1Q_JFCyRltoqac2wiDfUbZXCA_4geQKojbcJzddE4R_Y_2FBw";
        /**
         *  /*
         *         使用公钥解密，结果转换为字符串，使用默认字符集utf-8
         *      */

        String decodedData = DLRSAUtil.decryptedToStrByPublicKey(encodedData, DLRSAUtil.keyStrToPublicKey(tpub));
        System.out.println("解密后文字: \r\n" + decodedData);
    }
}